package com.lyj.weblog.web.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.lyj.weblog.admin.event.ReadArticleEvent;
import com.lyj.weblog.common.domain.dos.*;
import com.lyj.weblog.common.domain.mapper.*;
import com.lyj.weblog.common.enums.ResponseCodeEnum;
import com.lyj.weblog.common.exception.BizException;
import com.lyj.weblog.common.utils.PageResponse;
import com.lyj.weblog.common.utils.Response;
import com.lyj.weblog.web.convert.ArticleConvert;
import com.lyj.weblog.web.markdown.MarkdownHelper;
import com.lyj.weblog.web.model.vo.article.*;
import com.lyj.weblog.web.model.vo.category.FindCategoryListRspVO;
import com.lyj.weblog.web.model.vo.tag.FindTagListRspVO;
import com.lyj.weblog.web.service.ArticleService;
import com.lyj.weblog.web.utils.MarkdownStatsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private ArticleContentMapper articleContentMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;
    @Autowired
    private TagMapper tagMapper;
    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    /**
     * 获取首页文章分页数据
     * @param findIndexArticlePageListReqVO
     * @return
     */
    @Override
    public Response findArticlePageList(FindIndexArticlePageListReqVO findIndexArticlePageListReqVO) {
        final Long current = findIndexArticlePageListReqVO.getCurrent();
        final Long size = findIndexArticlePageListReqVO.getSize();
        //第一步：分页查询文章主体记录
        final Page<ArticleDO> articleDOPage = articleMapper.selectPageList(current, size, null, null, null);

        final List<ArticleDO> articleDOS = articleDOPage.getRecords();
        List<FindIndexArticlePageListRspVO>vos=null;
        if(!CollectionUtils.isEmpty(articleDOS)){
            //文章DO->VO
           vos=articleDOS.stream()
                         .map(articleDO ->  ArticleConvert.INSTANCE.convertDO2VO(articleDO))
                         .collect(Collectors.toList());
            // 拿到所有文章的 ID 集合
            final List<Long> articleIds = articleDOS.stream().map(ArticleDO::getId).collect(Collectors.toList());

            //第二步 : 设置文章所属分类
            // 查询所有分类
            final List<CategoryDO> categoryDOS  = categoryMapper.selectList(Wrappers.emptyWrapper());
            // 转 Map, 方便后续根据分类 ID 拿到对应的分类名称
            final Map<Long, String> categoryIdNameMap  = categoryDOS.stream().collect(Collectors.toMap(CategoryDO::getId, CategoryDO::getName));
            // 根据文章 ID 批量查询所有关联记录
            final List<ArticleCategoryRelDO> articleCategoryRelDOS = articleCategoryRelMapper.selectByArticleIds(articleIds);
            vos.forEach(vo->{
                final Long currArticleId = vo.getId();
                // 过滤出当前文章对应的关联数据
                final Optional<ArticleCategoryRelDO> optional  = articleCategoryRelDOS.stream().filter(rel -> Objects.equals(rel.getArticleId(), currArticleId)).findAny();
                //若不为空
                if(optional.isPresent()){
                    final ArticleCategoryRelDO articleCategoryRelDO = optional.get();
                    final Long categoryId = articleCategoryRelDO.getCategoryId();
                    // 通过分类 ID 从 map 中拿到对应的分类名称
                    final String categoryName  = categoryIdNameMap.get(categoryId);
                    final FindCategoryListRspVO findCategoryListRspVO= FindCategoryListRspVO
                            .builder().id(categoryId).name(categoryName).build();
                    //设置到当前vo类
                    vo.setCategory(findCategoryListRspVO);
                }
            });

            // 第三步：设置文章标签
            // 查询所有标签
            final List<TagDO> tagDOS = tagMapper.selectList(Wrappers.emptyWrapper());
            // 转 Map, 方便后续根据标签 ID 拿到对应的标签名称
            final Map<Long, String> mapIdNameMap  = tagDOS.stream().collect(Collectors.toMap(TagDO::getId, TagDO::getName));
            // 拿到所有文章的标签关联记录 todo
            List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleIds(articleIds);

            vos.forEach(vo->{
                final Long currArticleId = vo.getId();
                // 过滤出当前文章的标签关联记录
                List<ArticleTagRelDO> articleTagRelDOList= articleTagRelDOS.stream().filter(rel -> Objects.equals(rel.getArticleId(), currArticleId)).collect(Collectors.toList());
                List<FindTagListRspVO> findTagListRspVOS= Lists.newArrayList();
                // 将关联记录 DO 转 VO, 并设置对应的标签名称
                articleTagRelDOList.forEach(articleTagRelDO -> {
                    final Long tagId  = articleTagRelDO.getId();
                    final String tagName = mapIdNameMap.get(tagId);
                    final FindTagListRspVO findTagListRspVO = FindTagListRspVO.builder()
                                                                              .id(tagId)
                                                                              .name(tagName).build();
                    findTagListRspVOS.add(findTagListRspVO);

                });
                // 设置转换后的标签数据
                vo.setTags(findTagListRspVOS);
            });

        }

        return PageResponse.success(articleDOPage, vos);
    }
    /**
     * 获取文章详情
     * @param findArticleDetailReqVO
     * @return
     */
    @Override
    public Response findArticleDetail(FindArticleDetailReqVO findArticleDetailReqVO) {
        final Long articleId = findArticleDetailReqVO.getArticleId();
        final ArticleDO articleDO = articleMapper.selectById(articleId);
        // 判断文章是否存在
        if (Objects.isNull(articleDO)) {
            log.warn("==> 该文章不存在, articleId: {}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }
        // 查询正文
        final ArticleContentDO articleContentDO = articleContentMapper.selectByArticleId(articleId);
        String content = articleContentDO.getContent();
        // 计算 md 正文字数
        Integer totalWords = MarkdownStatsUtil.calculateWordCount(content);
        // DO 转 VO
        FindArticleDetailRspVO vo=FindArticleDetailRspVO.builder()
                .title(articleDO.getTitle())
                .createTime(articleDO.getCreateTime())
                .content(MarkdownHelper.convertMarkdown2Html(articleContentDO.getContent()))
                .readNum(articleDO.getReadNum())
                .totalWords(totalWords)
                .readTime(MarkdownStatsUtil.calculateReadingTime(totalWords))
                .build();
        // 查询所属分类
        final ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId);
        final CategoryDO categoryDO = categoryMapper.selectById(articleCategoryRelDO.getCategoryId());
        vo.setCategoryId(categoryDO.getId());
        vo.setCategoryName(categoryDO.getName());
        // 查询标签
        final List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId);
        final List<Long> tagIds  = articleTagRelDOS.stream().map(ArticleTagRelDO::getTagId).collect(Collectors.toList());
        final List<TagDO> tagDOS = tagMapper.selectByIds(tagIds);

        // 标签 DO 转 VO
        List<FindTagListRspVO>tagVOS=tagDOS.stream()
                .map(tagDO ->FindTagListRspVO.builder()
                    .id(tagDO.getId())
                    .name(tagDO.getName())
                    .build()).collect(Collectors.toList());
        vo.setTags(tagVOS);
        // 上一篇
        final ArticleDO preArticleDO  = articleMapper.selectPreArticle(articleId);
        if(Objects.nonNull(preArticleDO)){
            final FindPreNextArticleRspVO preArticleVO  = FindPreNextArticleRspVO.builder().articleId(preArticleDO.getId()).articleTitle(preArticleDO.getTitle()).build();
        vo.setPreArticle(preArticleVO);
        }
        // 下一篇
        final ArticleDO nextArticleDO  = articleMapper.selectNextArticle(articleId);
        if(Objects.nonNull(nextArticleDO)){
            final FindPreNextArticleRspVO nextArticleVo  =
                    FindPreNextArticleRspVO.builder().articleId(nextArticleDO.getId()).
                            articleTitle(nextArticleDO.getTitle()).build();
            vo.setNextArticle(nextArticleVo);
        }
        // 发布文章阅读事件
        eventPublisher.publishEvent(new ReadArticleEvent(this, articleId));

        return Response.success(vo);
    }



}
